package com.geline.mybatisplus.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.geline.mybatisplus.annotation.ExtractTable;
import com.geline.mybatisplus.annotation.TableSource;
import org.springframework.cglib.beans.BeanMap;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 通用service，支持主子表添加、更新操作
 * 实现思路：识别 @TableSource找到Entity自动实现CRUD
 *
 * @author mx
 * @date 2025/9/22
 */
public class DbService {

    public <V> boolean save(V object) {
        TableName tableName = object.getClass().getAnnotation(TableName.class);
        if(tableName!=null){
            Db.save(object);
            return true;
        }else {
            TableSource tableSource = object.getClass().getAnnotation(TableSource.class);
            if(tableSource==null){
                throw new RuntimeException(object.getClass().getSimpleName()+": no @TableSource !");
            }
            String mainTable = tableSource.value();
            TableInfo tableInfo = TableInfoHelper.getTableInfo(mainTable);
            Class<?> entityType = tableInfo.getEntityType();
            Object entity = BeanUtil.toBean(object, entityType);
            Db.save(entity);
            Serializable mainId = (Serializable) BeanMap.create(entity).get("id");
            //添加关联表记录
            saveOne2Many(object, mainTable, mainId, true);
            return true;
        }
    }

    public <V> boolean updateById(V object) {
        TableName tableName = object.getClass().getAnnotation(TableName.class);
        if(tableName!=null){
            Db.updateById(object);
            return true;
        }else {
            TableSource tableSource = object.getClass().getAnnotation(TableSource.class);
            if(tableSource==null){
                throw new RuntimeException(object.getClass().getSimpleName()+": no @TableSource !");
            }
            String mainTable = tableSource.value();
            TableInfo tableInfo = TableInfoHelper.getTableInfo(mainTable);
            Class<?> entityType = tableInfo.getEntityType();
            Object entity = BeanUtil.toBean(object, entityType);

            Serializable mainId = (Serializable) BeanMap.create(entity).get("id");
            //先删除旧的关联记录，再添加新的关联表记录
            saveOne2Many(object, mainTable, mainId, true);
            return Db.updateById(entity);
        }
    }

    public <V> boolean saveBatch(Collection<V> objects) {
        for (V object : objects) {
            save(object);
        }
        return true;
    }

    public <V> boolean saveOrUpdate(V object) {
        TableName tableName = object.getClass().getAnnotation(TableName.class);
        if(tableName!=null){
            Db.saveOrUpdate(object);
            return true;
        }else {
            TableSource tableSource = object.getClass().getAnnotation(TableSource.class);
            if (tableSource == null) {
                throw new RuntimeException(object.getClass().getSimpleName() + ": no @TableSource !");
            }
            String mainTable = tableSource.value();
            TableInfo tableInfo = TableInfoHelper.getTableInfo(mainTable);
            Class<?> entityType = tableInfo.getEntityType();
            Object entity = BeanUtil.toBean(object, entityType);

            Serializable mainId = (Serializable) BeanMap.create(entity).get("id");
            //先删除旧的关联记录，再添加新的关联表记录
            saveOne2Many(object, mainTable, mainId, true);
            return Db.saveOrUpdate(entity);
        }
    }

    public <V> boolean saveOrUpdate(Collection<V> objects) {
        for (V object : objects) {
            saveOrUpdate(object);
        }
        return true;
    }

    public <T> T getById(Serializable id, Class<T> entityClass) {
        return Db.getById(id, entityClass);
    }

    public <V> V getVOById(Serializable id, Class<V> voClass) {
        TableSource tableSource = voClass.getAnnotation(TableSource.class);
        String table = tableSource.value();
        TableInfo tableInfo = TableInfoHelper.getTableInfo(table);
        Class<?> entityType = tableInfo.getEntityType();
        Object object = Db.getById(id, entityType);
        return BeanUtil.toBean(object, voClass);
    }

    public <T> boolean removeById(Serializable id, Class<T> entityClass) {
        return Db.removeById(id, entityClass);
    }

    public <T> boolean removeByIds(Collection<Serializable> ids, Class<T> entityClass) {
        return Db.removeByIds(ids, entityClass);
    }

    public <T, V> List<V> list(AbstractWrapper<T, ?, ?> queryWrapper, Class<V> voClass) {
        List<T> list = Db.list(queryWrapper);
        return BeanUtil.copyToList(list, voClass);
    }

    public <T> List<Map<String, Object>> listMaps(AbstractWrapper<T, ?, ?> queryWrapper) {
        return Db.listMaps(queryWrapper);
    }

    public <T, V> List<V> listMaps(AbstractWrapper<T, ?, ?> queryWrapper, Class<V> voClass) {
        List<Map<String, Object>> maps = Db.listMaps(queryWrapper);
        return BeanUtil.copyToList(maps, voClass);
    }

    public <T> IPage<T> page(IPage page, AbstractWrapper<T, ?, ?> queryWrapper) {
        return Db.page(page, queryWrapper);
    }

    public <T, V> IPage<V> page(IPage page, AbstractWrapper<T, ?, ?> queryWrapper, Class<V> voClass) {
        IPage<T> pages = Db.page(page, queryWrapper);
        List<T> beans = pages.getRecords();
        List<V> voList = BeanUtil.copyToList(beans, voClass);
        Page pageNew = new Page();
        pageNew.setCurrent(pages.getCurrent());
        pageNew.setSize(pages.getSize());
        pageNew.setTotal(pages.getTotal());
        pageNew.setPages(pages.getPages());
        pageNew.setRecords(voList);
        return pageNew;
    }

    public <T> IPage<Map<String, Object>> pageMaps(IPage page, AbstractWrapper<T, ?, ?> queryWrapper) {
        return Db.pageMaps(page, queryWrapper);
    }

    public <T, V> IPage<V> pageMaps(IPage page, AbstractWrapper<T, ?, ?> queryWrapper, Class<V> voClass) {
        IPage<Map<String, Object>> pages = Db.pageMaps(page, queryWrapper);
        List<Map<String, Object>> maps = pages.getRecords();
        List<V> voList = BeanUtil.copyToList(maps, voClass);
        Page pageNew = new Page();
        pageNew.setCurrent(pages.getCurrent());
        pageNew.setSize(pages.getSize());
        pageNew.setTotal(pages.getTotal());
        pageNew.setPages(pages.getPages());
        pageNew.setRecords(voList);
        return pageNew;
    }

    private String getForeignKey(Class<?> entityType, String mainTable){
        ExtractTable[] annotations = entityType.getAnnotationsByType(ExtractTable.class);
        if(annotations==null || annotations.length==0){
            throw new RuntimeException(entityType.getSimpleName()+": can not find @ExtractTable !");
        }
        String foreignKey = null;
        for(ExtractTable annotation : annotations){
            String value = annotation.value();
            if(value.equals(mainTable)){
                foreignKey = annotation.foreignKey();
                break;
            }
        }
        if(StrUtil.isBlank(foreignKey)){
            throw new RuntimeException(entityType.getSimpleName()+": no @ExtractTable foreignKey !");
        }
        return foreignKey;
    }

    public void saveSubList(TableSource tableSource, Collection objects, String mainTable, Serializable mainId){
        String table = tableSource.value();
        TableInfo tableInfo = TableInfoHelper.getTableInfo(table);
        Class<?> entityType = tableInfo.getEntityType();
        String foreignKey = getForeignKey(entityType, mainTable);
        for(Object object : objects){
            Object entity = BeanUtil.toBean(object, entityType);
            String fieldName = StrUtil.toCamelCase(foreignKey);
            BeanMap.create(entity).put(fieldName, mainId);
            this.saveOrUpdate(entity);
        }
    }

    //添加关联表记录
    protected void saveOne2Many(Object object, String mainTable, Serializable mainId, boolean deleteOld){
        BeanMap beanMap = BeanMap.create(object);
        for(Field field : object.getClass().getDeclaredFields()) {
            Collection<Object> list = (Collection) beanMap.get(field.getName());
            if(list==null || list.isEmpty()){
                continue;
            }
            Class<?> type = field.getType();
            if(type.isAssignableFrom(Collection.class)) {
                Class<?> rowClass = null;
                Type genericType = field.getGenericType();
                if (genericType instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType) genericType;
                    Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                    rowClass = (Class<?>) actualTypeArguments[0];
                }
                if(rowClass!=null){
                    TableSource tableSource = rowClass.getAnnotation(TableSource.class);
                    if(tableSource!=null){
                        String table = tableSource.value();
                        TableInfo tableInfo = TableInfoHelper.getTableInfo(table);
                        Class<?> entityType = tableInfo.getEntityType();
                        String foreignKey = getForeignKey(entityType, mainTable);

                        if(deleteOld){
                            if(list.size()==0){
                                //删除关联表数据
                                QueryWrapper wrapper = new QueryWrapper(entityType);
                                wrapper.eq(foreignKey, mainId);
                                Db.remove(wrapper);
                            }else {
                                //1.先删除不存在的数据
                                List<Object> ids = list.stream().map(row -> BeanMap.create(row).get("id")).collect(Collectors.toList());
                                QueryWrapper wrapper = new QueryWrapper();
                                wrapper.eq(foreignKey, mainId);
                                wrapper.notIn("id", ids);
                                Db.remove(wrapper);
                            }
                        }
                        //保存子表数据
                        this.saveSubList(tableSource, list, mainTable, mainId);
                    }
                }
            }
        }
    }
}
